#include <boost/bind.hpp>
#include <boost/thread/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/compare.hpp>
#include <iostream>

#include "format.h"
#include "server.h"
#include "log.h"

server::server(const server_parameters::remote_point &_listen_point, std::size_t _io_service_pool_size,  uid_t _user, gid_t _group)
  : m_acceptor(m_io_service),
    m_new_con_connection( new con_connection(m_io_service, network_, sqlhandle_)),
	io_service_pool_size_(_io_service_pool_size)
{

  sqlhandle_.start(g_config.connect_host_,
		    g_config.connect_user_,
		    g_config.connect_password_,
		    g_config.connect_database_);
 
  
  boost::asio::ip::tcp::resolver resolver(m_acceptor.io_service());
  boost::asio::ip::tcp::resolver::query query(_listen_point.m_host_address, boost::lexical_cast<std::string>(_listen_point.m_port));
  boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
  
  m_acceptor.open(endpoint.protocol());
  m_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
  m_acceptor.bind(endpoint);
  m_acceptor.listen();

  if (_group && (setgid(_group) == -1))
  {
    throw std::logic_error("can not change process group id !");
  }

  if (_user && (setuid(_user) == -1))
  {
    throw std::logic_error("can not change process user id !");
  }
  
  m_acceptor.async_accept(
			m_new_con_connection->socket(),
			boost::bind(&server::handle_accept, this, boost::asio::placeholders::error)
		    );
}

void server::run()
{
    for (std::size_t i = 0; i < io_service_pool_size_; ++i)
    {
		boost::shared_ptr<boost::thread> thread(
							new boost::thread(
									boost::bind(&boost::asio::io_service::run, &m_io_service)
									)
								);
		threads_pool_.push_back(thread);
    }
    
    for (std::size_t i = 0; i < threads_pool_.size(); ++i)
    	threads_pool_[i]->join();
    	
}

void server::stop()
{
	m_acceptor.close();
	network_.close_all_connection();
    m_io_service.stop();
}

void server::handle_accept(const boost::system::error_code& e)
{
  if (!e)
  {
  
    try 
    {
    	m_new_con_connection->start();
    
    	m_new_con_connection.reset( new con_connection(m_io_service, network_, sqlhandle_) );

    }	
    catch(const boost::system::system_error &e)
    {
    	if (e.code() != boost::asio::error::not_connected)
    	{
    	    g_log.msg(MSG_NORMAL, format("Accept exception: %1").arg(e.what()));
    	}
	
    	m_new_con_connection.reset(new con_connection(m_io_service, network_, sqlhandle_));
    }
  }
  else
  {
	if (e == boost::asio::error::operation_aborted)
	{
	  return;
	}
    else if (e != boost::asio::error::not_connected)
    {
    	g_log.msg(MSG_NORMAL, format("Accept error: %1").arg(e.message()));
    }	
  }
  
   m_acceptor.async_accept(m_new_con_connection->socket(),
				boost::bind(&server::handle_accept, this, boost::asio::placeholders::error)
			    );
  
}
